unit plugins;

interface
uses CoreI;
Function Core_PluginExists(const GUID:TGUID):boolean; cdecl;
Function Core_RequirePlugin(const GUID:TGUID):pointer; cdecl;
Procedure Core_ScanForPlugins(); cdecl;
procedure Plugins_Init;
Procedure Core_FreePlugins(); cdecl;
implementation
uses SysUtils, lists, moduleloader;
type PPlugin=^TPlugin;
TPlugin=record
info:PPluginInfo;
                          initialized:boolean;
end;
var PluginsList:PList=nil;
function GetPluginByGUID(const GUID:TGUID):PPlugin;
var PWalk:PList;
begin
result:=nil;
PWalk:=PluginsList;
while PWalk<>nil do begin
                        if IsEqualGUID(PPlugin(PWalk^.Data)^.info^.GUID, GUID) then begin
result:=PPlugin(PWalk^.Data);
exit;
end else PWalk:=PWalk^.Next;
end;
end;

var _Plugin:PPluginInfo;
               Function Plugin_InitErrorHandler(var str:PChar; var code:cardinal):boolean; cdecl;
var s:string;
begin
result:=true;
s:=_Plugin^.Name + ': ' + str;
getmem(str, length(s)+1);
strCopy(str, PChar(s));
end;
procedure LoadPlugin(plugin:PPlugin);
begin
if not Plugin^.initialized then begin
if Plugin^.info^.Name <> '' then
log_write(PChar('Initializing ' + plugin^.info^.Name), ll_info);
try
if @plugin^.info^.Init <> nil then
Plugin^.info^.Init;
except
on e:Exception do Core_Error(PChar(e.Message), ECore_Init);
end;
Plugin^.initialized := true;
end;
end;

Procedure CallPluginsInitializeFunction();
Var PWalk:PList;
begin
core_RegisterErrorHandler(Plugin_InitErrorHandler);
PWalk:=PluginsList;
while PWalk<>nil do begin
_plugin:=PPlugin(PWalk^.Data)^.info;
LoadPlugin(PPlugin(PWalk^.Data));
PWalk:=PWalk^.Next;
end;
Core_UnregisterErrorHandler(Plugin_InitErrorHandler);
    end;
    Procedure RegisterPluginInfo(const name:string);
var info:PPluginInfo;
m:TModuleHandle;
plugin_GetInfo:TCore_Plugin_GetInfo;
Plugin:PPlugin;
l:cardinal;
i:integer;
    begin
m:=0;
LoadModule(m, PChar(Name));
if (m<>0) then begin
                                                  Plugin_GetInfo:=GetModuleSymbol(m, 'Plugin_GetInfo');
if Assigned(Plugin_GetInfo) then begin
Plugin_GetInfo(info, l);
for i:=1 to l do begin
if i>1 then
info:=PPluginInfo(cardinal(info) + sizeof(TPluginInfo));
if not Core_PluginExists(info^.GUID) then begin
                                              new(plugin);
                                              plugin^.info:=info;
                                              plugin^.initialized := false;
                                              list_add(PluginsList, Plugin);
                                              end else
Core_Error(PChar('Plugin with GUID ' + GUIDToString(info^.GUID) + ' dublicates existing plugin'), ECore_Init);
end;
end else UnloadModule(m);
end else log_write(pchar('Unable to load plugin ' + Name + ': ' + sysErrorMessage(GetLastError())), ll_warn);
    end;

Procedure Core_ScanForPlugins(); cdecl;
        var sr:TSearchRec;
    begin
{$ifdef MSWINDOWS}
           if FindFirst(ExtractFilePath(paramstr(0)) + '*.dll', faanyfile,sr) <> 0 then exit;
                                                                                                                repeat
if (sr.name='.') or (sr.name='..') then continue;
RegisterPluginInfo(sr.Name);
until FindNext(sr) <> 0;
{$endif}
log_write(PChar('Found ' + inttostr(list_size(PluginsList)) + ' plugins'), ll_info);
end;
    procedure Plugins_Init;
    begin
    Core_ScanForPlugins();
    CallPluginsInitializeFunction();
           end;

Function Core_PluginExists(const GUID:TGUID):boolean; cdecl;
begin
result:=GetPluginByGUID(GUID) <> nil;
end;

Procedure Core_FreePlugins(); cdecl;
var PWalk:PList;
begin
                                PWalk:=PluginsList;
                                while PWalk<>nil do begin
if @PPlugin(PWalk^.Data)^.info^.free <> nil then begin
log_write(pchar('Closing ' + PPlugin(PWalk^.Data)^.info^.Name), ll_info);
PPlugin(PWalk^.Data)^.info^.free();
end;
PPlugin(PWalk^.Data)^.initialized:=false;
                                PWalk:=PWalk^.Next;
                                end;
list_clear(PluginsList);
end;
Function Core_RequirePlugin;
var Plugin:PPlugin;
begin
result:=nil;
plugin:=GetPluginByGUID(GUID);
if plugin = nil then begin
Core_Error(PChar('required plugin not exists (GUID: ' + GuidToString(Guid) + ')'), ECore_Init);
exit;
end;
if not Plugin^.initialized then LoadPlugin(plugin);
result:=Plugin^.info^.InterfaceStruct;
end;

Function Core_GetPluginByGUID(Const GUID:TGUID):PPluginInfo; cdecl;
var plugin:PPlugin;
begin
Plugin:=GetPluginByGUID(GUID);
if Plugin <> nil then result:=Plugin^.info else result:=nil;
end;
end.
